package com.aichat.ai_chatbackend.utils;

import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis操作工具类  Redis可以存储键和五种数据类型  分别是 String(字符串) Hash(哈希) List(列表) Set(集合) ZSet(有序集合)
 */
@Slf4j
@Component
public class RedisUtil {
    @Resource
    private RedisTemplate redisTemplate;

    public static final long REDIS_DEFAULT_EXPIRE_TIME = 60 * 60;
    public static final TimeUnit REDIS_DEFAULT_EXPIRE_TIMEUNIT = TimeUnit.SECONDS;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class ZObjScore {
        private Object member;
        private Double score;
    }


    /**
     * 随机返回集合中count个元素的集合
     * @param key
     * @param count
     * @return
     */
    public Set<Object> srandmember(String key, Integer count) {
        return redisTemplate.opsForSet().distinctRandomMembers(key, count);
    }




    // String 相关操作 begin --------------------------------------------------------------------------------------------

    /**
     * 根据传入的类型获取实体类
     */
    public <T> T getObject(String key, Class<T> clazz) {
        String objectString = (String) redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(objectString)) {
            //将字符串转为java对象
            return JSONUtil.toBean(objectString, clazz);
        }
        return null;
    }

    /**
     * 存储实体类
     */
    public void setObject(String key,Object obj) {
        redisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(obj));
    }

    /**
     * 获取指定key的String类型value个数
     * @param key
     * @return String类型key的数量
     */
    public long getStringTypeKeyCountByPattern(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    // Hash 相关操作 begin --------------------------------------------------------------------------------------------

    /**
     * 获取map中的指定hashKey对应的数据
     * @param key
     * @param hashKey
     * @return
     */
    public Object hmGet(String key, String hashKey) {
        try {
            return redisTemplate.opsForHash().get(key, hashKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取map中所有的key
     * @param key
     * @return
     */
    public Set<Object> hmGetAllKeys(String key) {
        try {
            Set<Object> fields = redisTemplate.opsForHash().keys(key);
            return fields;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 查询hash中是否存在该key
     * @return
     */
    public Boolean hmExist(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey( key, hashKey);
    }

    /**
     * 把数据存储在map中指定hashKey的value
     * @param key
     * @param hashKey
     * @param value
     */
    public void hmPut(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put( key, hashKey, value);
    }

    /**
     * 删除map中指定hashKey
     * @param key
     * @param hashKeys
     * @return
     */
    public Long hDelete(String key, Object... hashKeys) {
        return redisTemplate.opsForHash().delete(key, hashKeys);
    }


    // SET 相关操作 begin -----------------------------------------------------------------------------------------------

    /**
     * set集合获取
     * @param key
     * @return
     */
    public Set<Object> getMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }



    /**
     * 集合set中是否存在目标对象
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean isMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 删除SET中的数据
     * @param key
     * @param value
     */
    public void delMember(String key, Object value) {
        redisTemplate.opsForSet().remove(key, value);
    }



    /**
     * 向SET中添加无过期时间的对象
     * @param key
     * @param value
     */
    public void addMember(String key, Object value) {
        redisTemplate.opsForSet().add(key, value);
    }


    /**
     * 存储简单数据类型
     * 不用更新的缓存信息
     * @param key
     * @param value
     */
    public void setValue(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }


    /**
     * 使用 默认有效期 和 默认时间单位 存储简单数据类型
     * @param key
     * @param value
     */
    public void setExValue(String key, Object value) {
        setExValue(key, value, REDIS_DEFAULT_EXPIRE_TIME, REDIS_DEFAULT_EXPIRE_TIMEUNIT);
    }

    /**
     * 使用 指定有效期 和 默认时间单位 存储简单数据类型
     * @param key
     * @param value
     * @param time
     */
    public void setExValue(String key, Object value, long time) {
        setExValue(key, value, time, REDIS_DEFAULT_EXPIRE_TIMEUNIT);
    }

    /**
     * 使用持续时间
     * @param key
     * @param value
     * @param time
     */
    public void setExValue(String key, Object value, Duration time) {
        setExValue(key, value, time);
    }

    /**
     * 使用 指定有效期 和 指定时间单位 存储简单数据类型
     * @param key
     * @param value
     * @param time
     * @param timeUnit
     */
    public void setExValue(String key, Object value, long time, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, time, timeUnit);
    }


    /**
     * 使用默认有效期存储实体类
     * @param key
     * @param value
     */
    public void setExObjectValue(String key, Object value) {
        String jsonString = JSONUtil.toJsonStr(value);
        setExValue(key, jsonString);
    }

    // ZSET 相关操作 begin ----------------------------------------------------------------------------------------------
    /**
     * 按时间从大到小取数据携带分数
     * @param key
     * @param start
     * @param end
     * @return
     */
    public List<ZObjScore> zReverangeWithScores(String key, long start, long end) {
        Set<ZSetOperations.TypedTuple<Object>> result = redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
        if (result == null) return null;
        List<ZObjScore> list = new ArrayList<>();
        for (ZSetOperations.TypedTuple<Object> tuple : result) {
            list.add(new ZObjScore(tuple.getValue(), tuple.getScore()));
        }
        return list;
    }


    /**
     * 查询某个元素的分数
     * @param key
     * @param value
     * @return
     */
    public Double zscore(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 获取整个集合元素个数
     * @param key
     * @return
     */
    public Long zCard(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }

    /**
     * 删除Set指定key下的对象
     * @param key
     * @param value
     */
    public void zsetDelMember(String key, Object value) {
        redisTemplate.opsForZSet().remove(key, value);
    }


    /**
     * 存入一条数据到sorted set    默认按时间排序
     * @param key
     * @param object
     */
    public boolean zset(String key, Object object){
        long now = System.currentTimeMillis();
        return this.zsetWithScore(key, object, now);
    }

    /**
     * 存入一条数据到sorted set    按分数排序
     * @param key
     * @param object
     */
    public boolean zsetWithScore(String key, Object object, double score){
        return redisTemplate.opsForZSet().add(key, object, score);
    }

    /**
     * 分数从大到小取排行榜
     * @param key
     * @param start
     * @param stop
     * @return
     */
    public Set<Object> zReverange(String key, long start, long stop) {
        return redisTemplate.opsForZSet().reverseRange(key, start, stop);
    }

    /**
     * 对 Sorted Set 中的成员进行点赞（score + 1）
     * @param key    Redis key（如 "comment_video:123"）
     * @param member 成员（如评论ID）
     * @return 增加后的 score
     */
    public double incrementScore(String key, Object member) {
        return redisTemplate.opsForZSet().incrementScore(key, member, 1);
    }


    /**
     * 集合zset中是否存在目标对象
     * @param key
     * @param value
     * @return
     */
    public Boolean zsetExist(String key, Object value) {
        Double d = zscore(key, value);
        return null != d;
    }

    /**
     * 对某个元素增加分数
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Double zincrby(String key, Object value, double score) {
        return redisTemplate.opsForZSet().incrementScore(key, value, score);
    }



    // GET 相关操作 begin -----------------------------------------------------------------------------------------------


    /**
     * 获取简单数据类型
     * @param key
     * @return
     */
    public Object getValue(Object key) {
        return redisTemplate.opsForValue().get(key);
    }


    // DEL 相关操作 begin -----------------------------------------------------------------------------------------------

    /**
     * 删除简单数据类型或实体类
     * @param key
     */
    public void delValue(String key) {
        redisTemplate.opsForValue().getOperations().delete(key);
    }

    // DEL 相关操作 end -----------------------------------------------------------------------------------------------

    // 通用 相关操作 begin -----------------------------------------------------------------------------------------------
    /**
     * 查询key是否存在
     *
     * @param redisKey
     * @return
     */
    public boolean isExist(String redisKey) {
        return redisTemplate.hasKey(redisKey);
    }

    /**
     * 删除指定前缀的所有key
     * @param prefix
     */
    public void deleteKeysWithPrefix(String prefix) {
        // 获取以指定前缀开头的所有键
        Set<String> keys = redisTemplate.keys(prefix + "*");
        // 删除匹配的键
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 从列表中弹出一个成员（队列头部）
     * @param key Redis键
     * @return 弹出的成员，如果列表为空则返回null
     */
    public String popMember(String key) {
        try {
            return (String) redisTemplate.opsForList().leftPop(key);
        } catch (Exception e) {
            log.error("Redis从队列中弹出成员失败: key={}", key, e);
            return null;
        }
    }

    /**
     * 向队列尾部添加一个成员
     * @param key Redis键
     * @param value 要添加的值
     */
    public void putMember(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, String.valueOf(value));
        } catch (Exception e) {
            log.error("Redis向队列添加成员失败: key={}, value={}", key, value, e);
        }
    }

    /**
     * 尝试获取分布式锁
     * @param lockKey 锁的键
     * @param requestId 请求标识
     * @param expireTime 过期时间，单位秒
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey, String requestId, long expireTime) {
        try {
            // 直接使用Spring Data Redis的API，更可靠
            Boolean result = redisTemplate.opsForValue().setIfAbsent(
                    lockKey, requestId, expireTime, TimeUnit.SECONDS);

            // 打印调试日志
            log.info("尝试获取锁 {}, 结果: {}", lockKey, result);

            return Boolean.TRUE.equals(result);
        } catch (Exception e) {
            log.error("获取Redis锁失败: key={}, 错误={}", lockKey, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 释放分布式锁
     * @param lockKey 锁的键
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean releaseLock(String lockKey, String requestId) {
        try {
            // 先检查当前锁是否由当前请求持有
            Object currentValue = redisTemplate.opsForValue().get(lockKey);
            if (requestId.equals(currentValue)) {
                return Boolean.TRUE.equals(redisTemplate.delete(lockKey));
            }
            return false; // 锁不存在或不是由当前请求持有
        } catch (Exception e) {
            log.error("释放Redis锁失败: key={}", lockKey, e);
            return false;
        }
    }



}
